package uk.co.hs.web.initializer;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;


public class HsInitializer implements WebApplicationInitializer
{
  public static final String MODULE_CORE = "core";
  private Logger mLogger = LoggerFactory.getLogger(getClass());

  /**
   * @see org.springframework.web.WebApplicationInitializer
   * #onStartup(javax.servlet.ServletContext)
   * {@inheritDoc}
   */
  public void onStartup(ServletContext aContainer) throws ServletException
  {
    //Context
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    ConfigurableEnvironment ce = rootContext.getEnvironment();
    rootContext.register(HsConfig.class);

    // limited to hs to avoid picking up V8 beans as well
    rootContext.scan("uk.co.hs");

    // Logback needs to be as early as possible
    aContainer.addListener(new ContextLoaderListener(rootContext));
    aContainer.addListener(new LogbackInitializerListener());

    List<String> profiles = new ArrayList<>();
    ce.setDefaultProfiles("single_node");

    // Determine the sign on profile from the Environment settings.
    profiles.add(rootContext.getEnvironment().getProperty("SignOnProfile"));
    String activeModules = rootContext.getEnvironment().getProperty("Modules");

    if (activeModules != null && !activeModules.equals(""))
    {
      String[] activeModulesAsArray = activeModules.split(",");
      profiles.addAll(Arrays.asList(activeModulesAsArray));
    }
    else
    {
      rootContext.getEnvironment().setDefaultProfiles(MODULE_CORE);
      profiles.add(MODULE_CORE);
    }
    rootContext.getEnvironment().setActiveProfiles(profiles.toArray(new String[]{}));

    //Listeners.
    aContainer.addListener(new VersionLoaderListener());
    aContainer.addListener(new ApplicationControlListener());

    //Spring Security
    DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("springSecurityFilterChain", rootContext);
    aContainer.addFilter("springSecurityFilterChain", delegatingFilterProxy).addMappingForUrlPatterns(null, true, "*");

    //Dispatcher
    DispatcherServlet dispatcherServlet = new DispatcherServlet(rootContext);
    ServletRegistration.Dynamic dispatcher = aContainer.addServlet("hs", dispatcherServlet);
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("*.htm");

    determineCorporateLogo(aContainer);
  }

  private void determineCorporateLogo(ServletContext ctx)
  {
    // Derive the image from local store
    InitialContext initialContext;
    String fullPath = "" ;
    String logoFilename = "";

    try
    {
      initialContext = new javax.naming.InitialContext();
      fullPath = (String) initialContext.lookup("java:comp/env/HealthAndSafety_rootFolder")  + "/config/logo";

      logoFilename = getLogoFilename(fullPath);

      // Try and get the default image if no corporate logo has been configured.
      if ( StringUtils.isEmpty(logoFilename) )
      {
        mLogger.info("No corporate logo configured. Retrieving default image ... ");

        fullPath = (String) initialContext.lookup("java:comp/env/HealthAndSafety_rootFolder")  + "/config/logo/default";
        logoFilename = getLogoFilename(fullPath);

        if ( StringUtils.isEmpty(logoFilename) )
        {
          throw new RuntimeException("Cannot find a logo file. Please configure a corporate logo or a default image. Shutting down application.");
        }
      }

      // Decode the file name (might contain spaces and on) and prepare file object.
      File image = new File(fullPath, logoFilename);

      // Get content type by filename.
      String contentType = ctx.getMimeType(image.getName());

      // Check if file is actually an image (avoid download of other files by hackers!).
      // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
      if (contentType == null || !contentType.startsWith("image"))
      {
        throw new RuntimeException("Warning: this is not an image! [" + logoFilename + "] Shutting down application.");
      }

      ctx.setAttribute("corporateLogo", image);
      ctx.setAttribute("logoContentType", contentType);
    }
    catch (NamingException e)
    {
      e.printStackTrace();
    }
  }

  /**
   * Find logo file on the given path
   *
   * @param fullPath
   * @return the name of the logo file or "" if not present
   */
  private String getLogoFilename(String fullPath)
  {
    String logoFilename = "";

    File f = new File(fullPath);

    FilenameFilter filter = new FilenameFilter()
    {
      @Override
      public boolean accept(File dir, String fileName)
      {
        String[] suffixes = new String[] { "tiff","gif","jpg","jpeg","png" };
        List<String> suffixList = Arrays.asList(suffixes);

        if ( StringUtils.lastIndexOf(fileName, ".") > 0 )
        {
          String suffix = StringUtils.substringAfterLast(fileName, ".");

          if ( suffixList.contains(suffix) )
          {
            return true;
          }
        }
        return false;
      }
    };

    String[] files = f.list(filter);

    if ( files.length == 0 )
    {
      mLogger.info("No image files found at " + fullPath);
    }
    else
    {
      mLogger.info("Corporate Logo image: " + files[0]);
      logoFilename = files[0];
    }

    return logoFilename;
  }
}
